<template>
  <div>
    <button @mousedown="startDrag('Start', $event)">开始节点</button>
    <button @mousedown="startDrag('End', $event)">结束节点</button>
    <button @mousedown="startDrag('Circle', $event)">循环节点</button>
    <button @mousedown="startDrag('Rect', $event)">任务节点</button>
    <button @mousedown="startDrag('polygon', $event)">条件节点</button>
    <button @click="deleteNode()">删除</button>
    <button @click="onUndo">撤销</button>
    <button @click="onRedo">重做</button>
    <button @click="savePic">生成图片</button>
    <button @click="saveFlow">保存流程图并生成java代码</button>
    <button @click="getJson">getJson</button>
    <div class="graph-drawer-box">
      <div ref="graphContainer"></div>
      <RightDrawer ref="rightDrawer"
        :drawerType="type" :selectCellNum="selectCellNum" :selectCell="selectCell" 
        :graph="graph" :grid="grid" @deleteNode="deleteNode"></RightDrawer>
    </div>
  </div>
</template>
<script>
import { Graph, Shape, FunctionExt, DataUri } from '@antv/x6';
// import insertCss from 'insert-css';
import dataJSON from './data1'
import data from './data1'
import { startDragToGraph } from './methods.js';
import RightDrawer from './right-drawer';
export default {
  name: 'index2Page',
  components: {
    RightDrawer
  },
  data() {
    return {
      selectCellNum: 0,
      contentWidth: 0,
      contentHeight: 0,
      graph: null,
      type: 'grid',
      selectCell: '',
      connectEdgeType: {  //连线方式
        connector: 'normal',
        router: {
          // name: ''
          name: 'normal'
        }
      },
      grid: { // 网格设置
        showGrid: true,
        size: 10,      // 网格大小 10px
        visible: true, // 渲染网格背景
        type: 'mesh',
        args: {
          color: '#D0D0D0',
          thickness: 1,     // 网格线宽度/网格点大小
          factor: 10,
        },
      },
      canUndo: false,
      canRedo: false,
      haveStart: false,  //已有开始或结束
      haveEnd: false,
    }
  },
  mounted() {
    this.initX6()
  },
  methods: {
    // initGraph() {
    //   this.graph = new Graph({
    //     container: document.getElementById('container'),
    //     width: 800,
    //     height: 600,
    //     background: {
    //       color: '#F2F7FA'
    //     }
    //   })
    //   console.log(dataJSON)
    //   this.graph.fromJSON(dataJSON) // 渲染元素　　　 this.graph.centerContent() // 居中显示
    // },
    // 计算main主体高度
    getMainHeight(e) {
      /* this.contentHeight = Math.max(window.innerHeight - 60, 400);
      this.contentWidth = window.innerWidth - 250;
      if (e) {
        //画布调整大小
        this.graph.resize(this.contentWidth, this.contentHeight);
      } */
      this.contentWidth = this.$refs['graphContainer'].parentNode.offsetWidth;
      this.contentHeight = 910;
    },
    initX6() {
      this.type = 'grid';
      this.selectCell = '';
      let _that = this;
      // window.addEventListener('resize', this.debounceHeight);
      this.graph = new Graph({
        container: this.$refs['graphContainer'],
        width: 800,
        height: 600,
        grid: this.grid,
        panning: {
          enabled: true,
          modifiers: 'shift'
        }, //平移
        resizing: { //调整节点宽高
          enabled: true,
          orthogonal: false,
        },
        selecting: true, //可选
        snapline: true,
        interacting: {
          edgeLabelMovable: true
        },
        connecting: { // 节点连接
          anchor: 'center',
          connectionPoint: 'anchor',
          allowBlank: false,
          snap: true,
          createEdge() {
            return new Shape.Edge({
              attrs: {
                line: {
                  stroke: '#1890ff',
                  strokeWidth: 1,
                  targetMarker: {
                    name: 'classic',
                    size: 8
                  },
                  strokeDasharray: 0, //虚线
                  style: {
                    animation: 'ant-line 30s infinite linear',
                  },
                },
              },
              label: {
                text: ''
              },
              connector: _that.connectEdgeType.connector,
              router: {
                name: _that.connectEdgeType.router.name || ''
              },
              zIndex: 0
            })
          },
        },
        highlighting: {
          magnetAvailable: {
            name: 'stroke',
            args: {
              padding: 4,
              attrs: {
                strokeWidth: 4,
                stroke: '#6a6c8a'
              }
            }
          }
        },
        //滚动
        scroller: {
          enabled: false
        },
        //小地图
        minimap: {
          enabled: !this.readonly,
          container: !this.readonly ? this.$refs['rightDrawer'].$refs['miniMap'] : '',
          width: 300,
          height: 150,
          padding: 5
        },
        //缩放
        mousewheel: {
          enabled: true,
          modifiers: ['ctrl', 'meta'],
        },
        // 历史
        history: {
          enabled: true,
        },
        //框选
        selecting: {
          enabled: true,
          rubberband: true
        },
      });
      // insertCss(`
      //         @keyframes ant-line {
      //           to {
      //               stroke-dashoffset: -1000
      //           }
      //         }
      //       `)
      this.graph.fromJSON(data);
      this.graph.history.redo();
      this.graph.history.undo();
      // 鼠标移入移出节点
      this.graph.on('node:mouseenter', FunctionExt.debounce(() => {
        const container = this.$refs['graphContainer']
        const ports = container.querySelectorAll('.x6-port-body')
        this.showPorts(ports, true)
      }), 500);
      this.graph.on('node:mouseleave', () => {
        const container = this.$refs['graphContainer']
        const ports = container.querySelectorAll('.x6-port-body')
        this.showPorts(ports, false)
      });
      this.graph.on('blank:click', () => {
        this.type = 'grid';
        //选中网格需清空选中节点的数据
        this.selectCell = '';
        console.log('blank:click')
      });
      this.graph.on('cell:click', ({ cell }) => {
        this.type = cell.isNode() ? 'node' : 'edge';
        this.$emit('cellClick', cell);
        console.log('cell:click', cell, cell.isNode(), cell.attrs.nodeData, cell)
      });
      //监听框选
      this.graph.on('selection:changed', args => {
        //记录当前选中的数量
        this.selectCellNum = args.selected.length;
        if (this.selectCellNum > 1) {
          //只有选中一个节点时才允许修改节点信息
          this.type = 'grid';
          this.selectCell = '';
        } else {
          this.selectCell = args.added[0];
        }
        args.added.forEach(cell => {
          // this.selectCell = cell
          if (cell.isEdge()) {
            cell.isEdge() && cell.attr('line/strokeDasharray', 5) //虚线蚂蚁线
            cell.addTools([
              {
                name: 'vertices',
                args: {
                  padding: 4,
                  attrs: {
                    strokeWidth: 0.1,
                    stroke: '#2d8cf0',
                    fill: '#ffffff',
                  }
                },
              },
            ])
          }
        })
        args.removed.forEach(cell => {
          cell.isEdge() && cell.attr('line/strokeDasharray', 0);  //正常线
          cell.removeTools();
        })
      });
      /**
       * 处理history
       */
      //监听撤销
      this.graph.history.on('undo', args => {
        // code here
        this.$emit('historyUndo', args);
      });
      //监听重做
      this.graph.history.on('redo', args => {
        // code here
        this.$emit('historyRedo', args);
      });
      this.graph.history.on('change', () => {
        this.historyChange();
      });
      // 监听节点新增
      this.graph.on('node:added', args => {
        const { cell } = args;
        // 检查禁用情况
        this.checkNode();
        //选中
        this.graph.resetSelection(cell);
        this.type = 'node';
        const data = cell.store.data;
        switch (data.attrs.nodeData.type) {
          case 'Start':
            //开始
            this.haveStart = true;
            break;
          case 'End':
            //结束
            this.haveEnd = true;
            break;
        }
      });
      //监听节点删除
      this.graph.on('node:removed', args => {
        const { cell } = args;
        const data = cell.store.data;
        // 检查禁用情况
        this.checkNode();
        switch (data.attrs.nodeData.type) {
          case 'Start':
            //开始
            this.haveStart = false;
            break;
          case 'End':
            //结束
            this.haveEnd = false;
            break;
        }
      });
      //边连接
      this.graph.on('edge:connected', args => {
        //选中节点
        this.graph.resetSelection(args.edge);
        this.type = 'edge';
        console.log('edge:connected', args.edge)
      })
    },
    showPorts(ports, show) {
      for (let i = 0, len = ports.length; i < len; i = i + 1) {
        ports[i].style.visibility = show ? 'visible' : 'hidden'
      }
    },    // 拖拽生成正方形或者圆形
    startDrag(type, e) {
      if (this.disabled) {
        return;
      }
      //只能有一个开始和结束
      if (type === 'Start') {
        if (this.haveStart) {
          return;
        }
      } else if (type === 'End') {
        if (this.haveEnd) {
          return;
        }
      }
      startDragToGraph(this.graph, type, e);
    },
    // 删除节点
    deleteNode() {
      if (this.disabled) {
        return;
      }
      const cell = this.graph.getSelectedCells();
      this.graph.removeCells(cell);
      this.type = 'grid';
    },
    // 保存png
    savePic() {
      this.$nextTick(() => {
        this.graph.toPNG(dataUri => {
          // 下载
          DataUri.downloadDataUri(dataUri, this.title + '.png');
        }, {
          backgroundColor: 'white',
          padding: {
            top: 50,
            right: 50,
            bottom: 50,
            left: 50
          },
          quality: 1,
          copyStyles: false
        })
      })
    },
    /**
     * 检查是否可以禁用关联模型
     * 当节点中存在非开始和结束节点时不允许编辑 
     */ 
     checkNode(checkStartAndEnd) {
      const node = this.graph.getNodes();
      const ignores = ['Start', 'End'];
      let diabledEntity = false;
      console.log('node', node)
      for (let i = node.length - 1; i > -1; i--) {
        if (ignores.indexOf(node[i].attrs.nodeData.type) < 0) {
          diabledEntity = true;
          if (!checkStartAndEnd) {
            break;
          }
        } else if (checkStartAndEnd) {
          if (node[i].attrs.nodeData.type === 'Start') {
            this.haveStart = true;
          } else {
            this.haveEnd = true;
          }
        }
      }
      this.diabledEntity = diabledEntity;
    },
    //流程图完整性校验
    checkFlow() {
      const graphToJson = this.graph.toJSON();
      const { cells } = graphToJson;
      /**
       * 1、完整的流程图必需包含开始节点和结束节点且仅有一个
       * 2、每个节点必定有至少一条与其他节点的连接线；
       * 3、开始节点只能是连接线起点
       * 4、结束节点只能是连接线终点
       */
      let errs = [], //错误消息集合
        nodeList = [],
        edgeList = [],
        startNum = 0,
        endNum = 0,
        len = cells.length,
        alls = [],   //所有节点
        edges = [];  //与其他节点有连接关系的节点集合（不包含自己与自己连接的）
      if (len === 0) {
        errs.push('画布不能为空');
      } else {
        for (let i = 0; i < len; i++) {
          //非连线(节点)
          if (cells[i].shape !== 'edge') {
            //节点数据
            const nodeData = cells[i].attrs.nodeData;
            //节点名称
            const nodeText = cells[i].attrs.label.text;
            const flowNode = nodeData.flowNode;
            if (nodeData.type === 'Start') {
              startNum++;
            } else if (nodeData.type === 'End') {
              endNum++;
            } else if (nodeData.type === 'Rect') {
              let errTxt = '任务节点【' + nodeText + '】';
              switch (flowNode.flowNodeType) {
                case '':
                  errs.push(errTxt + '未选择节点类型');
                  break;
                case '2':
                  //执行规则组
                  if (flowNode.groupIdentify === '') {
                    errs.push(errTxt + '未选择执行规则组');
                  }
                  break;
                case '6':
                  //子流程
                  if (flowNode.childFlowIdentify === '') {
                    errs.push(errTxt + '未选择子流程');
                  }
                  break;
                case '7':
                  //源码节点
                  if (flowNode.methodName === '') {
                    errs.push(errTxt + '未选择方法');
                  } else if (flowNode.hasMethodParams && flowNode.methodParams === '') {
                    errs.push(errTxt + '方法未输入参数');
                  }
                  break;
              }
            } else if (nodeData.type === 'polygon') {
              const errTxt = '条件节点【' + nodeText + '】';
              //处理条件节点的边
              let conditionEdges = this.graph.getOutgoingEdges(cells[i].id);
              console.log('conditionEdges', conditionEdges)
              if (conditionEdges) {
                conditionEdges = conditionEdges.filter(item => {
                  return item.source.cell !== item.target.cell;
                });
                let trueNum = 0,
                  falseNum = 0;
                conditionEdges.forEach(item => {
                  console.log('item', item)
                  let labelText = item.store.data.labels ? item.store.data.labels[0].text != undefined ? item.store.data.labels[0].text : item.store.data.labels[0].attrs.label.text : '';
                  if (labelText === '是') {
                    trueNum++;
                  } else if (labelText === '否') {
                    falseNum++;
                  }
                });
                if (trueNum < 1) {
                  errs.push(errTxt + '缺少是判断的连线');
                } else if (trueNum > 1) {
                  errs.push(errTxt + '过多是判断的连线');
                }
                if (falseNum < 1) {
                  errs.push(errTxt + '缺少否判断的连线');
                } else if (falseNum > 1) {
                  errs.push(errTxt + '过多否判断的连线');
                }
              }
              //校验条件表单
              if (flowNode.flowJudges.length < 1) {
                errs.push(errTxt + '未选择判断条件');
              }
            }
            alls.push({
              id: cells[i].id,
              type: cells[i].attrs.nodeData.type
            });
            nodeList.push(cells[i]);
          } else {
            //连接线
            if (cells[i].source.cell !== cells[i].target.cell) {
              edges.push({
                source: cells[i].source.cell,
                target: cells[i].target.cell
              });
            }
            edgeList.push(cells[i]);
          }
        }

        if (startNum !== 1) {
          errs.push('开始节点必需存在，且仅有一个');
        }
        if (endNum !== 1) {
          errs.push('结束节点必需存在，且仅有一个');
        }

        let startEdgeErr = false,
          endEdgeErr = false,
          edgeErr = false;
        //遍历所有节点
        alls.forEach(item => {
          let hasEdge = false;
          for (let i = 0, len = edges.length; i < len; i++) {
            if (item.id === edges[i].source || item.id === edges[i].target) {
              hasEdge = true;
            }
            if (item.type === 'Start') {
              if (item.id === edges[i].target && !startEdgeErr) {
                startEdgeErr = true;
                errs.push('开始节点不能为连线终点');
              }
            } else if (item.type === 'End') {
              if (item.id === edges[i].source && !endEdgeErr) {
                endEdgeErr = true;
                errs.push('结束节点不能为连线起点');
              }
            }
          }
          if (!hasEdge && !edgeErr) {
            edgeErr = true;
            errs.push('节点间必需有连接线关联');
          }
        });
      }
      return {
        graphToJson,
        nodeList,
        edgeList,
        errs
      };
    },
    //保存流程图
    saveFlow() {
      // 先取消所有选中的节点和边, 防止把选中状态一起保存了
      this.graph.cleanSelection();
      const { errs, nodeList, edgeList, graphToJson } = this.checkFlow();
      console.log('nodeList', nodeList)
      console.log('edgeList', edgeList)
      console.log('saveData', {
        nodes: nodeList,
        edges: edgeList
      })
    },
    //历史状态改变
    historyChange() {
      this.canUndo = this.graph.history.canUndo();
      this.canRedo = this.graph.history.canRedo();
    },
    //撤销
    onUndo() {
      if (this.disabled) {
        return;
      }
      this.graph.history.undo();
    },
    //重做
    onRedo() {
      if (this.disabled) {
        return;
      }
      this.graph.history.redo();
    },
    getJson() {
      console.log(this.graph.toJSON());
    }
  }
}
</script>
<style lang="scss" scoped>
.graph-drawer-box{
  display: flex;
}
</style>